package util

import (
	"bytes"
	"encoding/binary"
	"encoding/gob"
	"reflect"
	"strconv"

	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"
)

//Str2int 打印并返回错误日志
func Str2int(str string, val int) int {
	intVal, err := strconv.Atoi(str)
	if err != nil {
		return val
	}
	return intVal
}

//Bytes2Struct 对象或基本类型反序列化
func Bytes2Struct(toConvert interface{}, buffer []byte) (interface{}, error) {
	if toConvert == nil {
		return buffer, nil
	}
	if buffer == nil {
		return nil, errors.Errorf("bytes should not be nil")
	}
	if message, ok := toConvert.(proto.Message); ok {
		err := proto.Unmarshal(buffer, message)
		if err != nil {
			return nil, errors.Errorf("protobuf serialize fail: %s", err)
		}
		return message, nil
	}
	if _, ok := toConvert.(uint16); ok {
		return binary.BigEndian.Uint16(buffer), nil
	}
	if _, ok := toConvert.(uint64); ok {
		return binary.BigEndian.Uint64(buffer), nil
	}
	if _, ok := toConvert.(string); ok {
		return string(buffer), nil
	}
	if _, ok := toConvert.(bool); ok {
		return strconv.ParseBool(string(buffer))
	}
	decoder := gob.NewDecoder(bytes.NewReader(buffer)) //创建解密器
	errDecode := decoder.Decode(toConvert)             //解密
	if errDecode != nil {
		return nil, errors.Errorf("original serialize fail: %s", errDecode.Error())
	}
	return toConvert, nil
}

//Struct2Bytes 采用protobuf序列化
func Struct2Bytes(in interface{}) ([]byte, error) {
	if in == nil {
		return nil, nil
	}
	//grpc序列化
	inMessage, ok := in.(proto.Message)
	if ok && reflect.ValueOf(inMessage).IsNil() {
		return []byte{}, nil
	}
	if ok {
		return proto.Marshal(inMessage)
	}
	//uint16序列化
	inUint16, ok := in.(uint16)
	if ok {
		b := make([]byte, 2)
		binary.BigEndian.PutUint16(b, inUint16)
		return b, nil
	}
	//uint64序列化
	inUint64, ok := in.(uint64)
	if ok {
		b := make([]byte, 8)
		binary.BigEndian.PutUint64(b, inUint64)
		return b, nil
	}
	//string序列化
	inString, ok := in.(string)
	if ok {
		return []byte(inString), nil
	}
	//bool序列化
	if inBool, ok := in.(bool); ok {
		return []byte(strconv.FormatBool(inBool)), nil
	}
	//其他基本类型序列化
	s := make([]byte, 0)
	buf := bytes.NewBuffer(s)
	err := binary.Write(buf, binary.BigEndian, in)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}
